package com.zon.len.mp.extend.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import ma.glasnost.orika.BoundMapperFacade;
import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import ma.glasnost.orika.metadata.MapperKey;
import ma.glasnost.orika.metadata.Type;
import ma.glasnost.orika.metadata.TypeFactory;
import org.springframework.cglib.beans.BeanCopier;

/**
 * @author ZL since on 2021/4/2 下午12:43 desc 字节码深copy
 */
@SuppressWarnings("all")
public final class ObjectCopy {

  private ObjectCopy() {
  }

  private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder()
      .build();

  static {
    MAPPER_FACTORY.getConverterFactory().registerConverter(new CustomCloneConverter<>());
  }

  private static final Object CLASS_MAP_REGISTER_LOCK = new Object();

  static {
    MAPPER_FACTORY.getConverterFactory().registerConverter(new CustomCloneConverter<>());
  }

  private static final Map<String, BeanCopier> COPIER_CACHE = new ConcurrentHashMap<>();
  /**
    * 推荐使用，spring的浅copy 底层是基于cglib字节码 copy 框架
    */
  public static <S, T> T map(S source, Supplier<T> targetSupplier) {
    final T target = targetSupplier.get();
    String key = genKey(source.getClass(), target.getClass());
    BeanCopier beanCopier;
    if (COPIER_CACHE.containsKey(key)) {
      beanCopier = COPIER_CACHE.get(key);
    } else {
      beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
      COPIER_CACHE.put(key, beanCopier);
    }
    beanCopier.copy(source, target, null);
    return target;
  }

  private static String genKey(Class<?> srcClazz, Class<?> tgtClazz) {
    return srcClazz.getName() + tgtClazz.getName();
  }



  public static <S, T> List<T> map(Collection<S> source, Class<T> target) {
    Objects.requireNonNull(source, "source must be not null");
    List<T> list = new ArrayList<>(source.size());
    for (Object o : source) {
      T result = map(o, target);
      list.add(result);
    }

    return list;
  }

  public static <S, T> T map(S source, Class<T> target) {
    Objects.requireNonNull(source, "source must be not null");
    Class<S> sourceClass = (Class<S>) source.getClass();
    MapperKey mapperKey = getMapperKey(target, sourceClass);
    if (MAPPER_FACTORY.getClassMap(mapperKey) == null) {
      synchronized (CLASS_MAP_REGISTER_LOCK) {
        if (MAPPER_FACTORY.getClassMap(mapperKey) == null) {
          registerClassMap(target, sourceClass);
        }
      }
    }
    BoundMapperFacade<S, T> mapperFacade = MAPPER_FACTORY.getMapperFacade(sourceClass, target);
    return mapperFacade.map(source);
  }

  private static <S, T> MapperKey getMapperKey(Class<T> tc, Class<S> sc) {
    return new MapperKey(TypeFactory.valueOf(sc), TypeFactory.valueOf(tc));
  }

  private static <S, T> void registerClassMap(Class<T> tc, Class<S> sc) {
    ClassMapBuilder<S, T> classMapBuilder = MAPPER_FACTORY.classMap(sc, tc);
    classMapBuilder.byDefault().register();
  }
  /**
   * 自定义转换, 如果后续需要进行扩展 如果目标映射字段是Obj类型直接赋值
   */
  public static class CustomCloneConverter<S> extends CustomConverter<S, Object> {

    @Override
    public Object convert(S source, Type<?> destinationType, MappingContext mappingContext) {
      return source;
    }
  }

}
